package com.wordpress.hybrid.util;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.webkit.URLUtil;
import android.webkit.WebResourceResponse;

import org.apache.http.NameValuePair;
import org.apache.http.conn.util.InetAddressUtils;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

/**
 * 统一处理网络请求和网络状态查询，方便统计
 *
 */
public class NetworkUtil {

    static final String TAG = "NetworkUtil";

    static final String NAME_PREF = "preference_multi_process_network";

    public static final String PREF_NETWORK_ALLOW = "network_allow";
    private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
    private static final String ENCODING_VALUES = "gzip,deflate,sdch";

    public static final class BadStatusCodeException extends IOException {

        private static final long serialVersionUID = 1L;
        private final int mStatusCode;
        public BadStatusCodeException(int code) {
            mStatusCode = code;
        }

        public int getStatusCode() {
            return mStatusCode;
        }

    };

    public enum NetState {
        WIFI,
        MN2G,
        MN3G,
        MN4G,
        NONE;
    }

    private static Object sNetWorkPermissionLock = new Object();

//    /**
//    * 获得网络类型 {@link ConnectivityManager}
//    * @param context
//    * @return
//    */
//    public static int getActiveNetworkType(Context context) {
//        return SecurityGuardian.getActiveNetworkType(context);
//    }
//
//    /**
//     *  返回可读的网络类型，type-subtype
//     */
//    public static String getActiveNetworkTypeName(Context context) {
//        return SecurityGuardian.getActiveNetworkTypeName(context);
//    }


    /**
     * 通过network type 得到相应的非 wifi 的network class，包括2g, 3g, 4g。
     *
     * @return
     */
//    public static NetState getNetState(Context context) {
//        try {
//            ConnectivityManager connManager = (ConnectivityManager) context
//                    .getSystemService(Context.CONNECTIVITY_SERVICE);
//            NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
//            if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) {
//                return NetState.NONE;
//            }
//            // 非收费网络，表明是wifi状态
//            if (Build.VERSION.SDK_INT >= 16) {
//                if (!connManager.isActiveNetworkMetered()) {
//                    return NetState.WIFI;
//                }
//            } else {
//                if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
//                    return NetState.WIFI;
//                }
//            }
//            int networkType = TelephonyUtils.getDefaultDataNetworkType();
//            return getNetworkClass(networkType);
//        } catch (Exception e) {
//        }
//        return NetState.NONE;
//    }

    private static NetState getNetworkClass(int networkType) {
        switch (networkType) {
            case TelephonyManager.NETWORK_TYPE_GPRS:
            case TelephonyManager.NETWORK_TYPE_EDGE:
            case TelephonyManager.NETWORK_TYPE_CDMA:
            case TelephonyManager.NETWORK_TYPE_1xRTT:
            case TelephonyManager.NETWORK_TYPE_IDEN:
                return NetState.MN2G;
            case TelephonyManager.NETWORK_TYPE_UMTS:
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
            case TelephonyManager.NETWORK_TYPE_HSDPA:
            case TelephonyManager.NETWORK_TYPE_HSUPA:
            case TelephonyManager.NETWORK_TYPE_HSPA:
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
            case TelephonyManager.NETWORK_TYPE_EHRPD:
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                return NetState.MN3G;
            case TelephonyManager.NETWORK_TYPE_LTE:
                return NetState.MN4G;
            default:
                return NetState.NONE;
        }
    }

    /**
     * 获得IP地址
     *
     * @return
     */
    public static String getLocalIpAddress() {
        try {
            String ipv4;
            List<NetworkInterface> nilist = Collections.list(
                    NetworkInterface.getNetworkInterfaces());
            for (NetworkInterface ni : nilist) {
                List<InetAddress> ialist = Collections.list(ni.getInetAddresses());
                for (InetAddress address : ialist) {
                    if (!address.isLoopbackAddress()
                            && InetAddressUtils.isIPv4Address(ipv4 = address.getHostAddress())) {
                        return ipv4;
                    }
                }
            }
        } catch (Exception ex) {
            Log.e(TAG, "getLocalIpAddress ex.", ex);
        }
        return null;
    }

//    /**
//    * 判断是否有可用网络
//    * @param context
//    * @return
//    */
//    public static boolean isActive(Context context) {
//        return getActiveNetworkType(context) >= 0;
//    }
//
//    /**
//    * 判断是否是收费网络
//    * @param context
//    * @return
//    */
//    public static boolean isActiveNetworkMetered(Context context) {
//        boolean metered = SecurityGuardian.isActiveNetworkMetered(context);
//        if (metered) {
//            TrafficProxy trafficProxy = TrafficProxy.Holder.get(null);
//            if (trafficProxy != null && trafficProxy.useProxy()) {
//                // 如果是数据网络，且已经开通了流量包，则使用流量包代理可免收流量，返回false
//                metered = false;
//            }
//        }
//        return metered;
//    }
//
//    public static boolean isDataNetwork(Context context) {
//        NetState state = getNetState(context);
//        switch (state) {
//            case MN4G:
//            case MN3G:
//            case MN2G:
//                return true;
//
//            default:
//                return false;
//        }
//    }

    /**
    * UTF-8 编码，失败时返回""
    * @param src
    * @return
    */
    public static String encode(String src) {
        if (src != null) {
            try {
                return URLEncoder.encode(src, "UTF-8");
            } catch (UnsupportedEncodingException e) {
            }
        }

        return "";
    }

    /**
     * UTF-8 解码，失败时返回""
     * @param src
     * @return
     */
     public static String decode(String src) {
         if (src != null) {
             try {
                 return URLDecoder.decode(src, "UTF-8");
             } catch (UnsupportedEncodingException e) {
             }
         }

         return "";
     }

    /**
    * 将args通过connector连接
    * @param connector
    * @param args
    * @return
    */
    public static String concat(String connector, Collection<String> args) {
        final StringBuilder sb = new StringBuilder();
        if (args != null && !args.isEmpty()) {
            for (String v : args) {
                if (v != null) {
                    sb.append(v);
                }

                sb.append(connector);
            }

            final int len = sb.length();
            final int connectorLen = connector.length();
            sb.delete(len - connectorLen, len);
        }
        return sb.toString();
    }

    /**
    * 拼接url
    * @param url
    * @param nameValues
    * @return
    */
    public static String concatQueryMap(String url, Map<String, String> nameValues) {
        if (nameValues == null || nameValues.isEmpty()) {
            return url;
        }

        Uri.Builder builder = Uri.parse(url).buildUpon();
        for (Map.Entry<String, String> entry : nameValues.entrySet()) {
            builder.appendQueryParameter(String.valueOf(entry.getKey()), entry.getValue());
        }

        return builder.build().toString();
    }

    public static String concatPath(String prefix, String... args) {
        if (args == null || args.length == 0) {
            return prefix;
        }
        return concatPath(prefix, Arrays.asList(args));
    }

    public static String concatPath(String prefix, Collection<String> args) {
        if (args == null || args.isEmpty()) {
            return prefix;
        }

        Uri.Builder builder = Uri.parse(prefix).buildUpon();
        for (Object arg : args) {
            builder.appendPath(String.valueOf(arg));
        }

        return builder.build().toString();
    }

    public static String concatQuery(String prefix, String... args) {
        if (args == null || args.length == 0) {
            return prefix;
        }

        Uri.Builder builder = Uri.parse(prefix).buildUpon();
        for (int i = 0; i < args.length; i += 2) {
            builder.appendQueryParameter(String.valueOf(args[i]), String.valueOf(args[i + 1]));
        }

        return builder.build().toString();
    }


    public static InputStream doHttpGet(final String strUrl)
    throws URISyntaxException, IOException {
        return doHttpGet(strUrl, null);
    }

    public static InputStream doHttpGet(final String strUrl, NameValuePair cookie)
    throws URISyntaxException, IOException {
        if (cookie == null) {
            return doHttpGet(strUrl, -1, -1, null, null);
        }
        return doHttpGet(strUrl, -1, -1, null, Arrays.asList(new NameValuePair[] { cookie } ));
    }

    public static InputStream doHttpGet(final String strUrl, int connTimeOut, int soTimeOut, long[] len)
    throws URISyntaxException, IOException {
        return doHttpGet(strUrl, connTimeOut, soTimeOut, len, null);
    }

    public static String doHttpGetForString(final String strUrl)
    throws URISyntaxException, IOException {
        final InputStream is = doHttpGet(strUrl);
        if (is != null) {
            try {
                return StreamHelper.toString(is);
            } finally {
                StreamHelper.closeSafe(is);
            }
        }
        return null;
    }

    /**
     * 向服务端提交HttpGet请求
     */
    public static InputStream doHttpGet(final String strUrl, int connTimeOut, int soTimeOut, long[] len,
            List<NameValuePair> headers)
    throws URISyntaxException, IOException {
        if (!(URLUtil.isHttpUrl(strUrl) || URLUtil.isHttpsUrl(strUrl))) {
            return null;
        }

        InputStream result = null;

        HttpURLConnection conn = HttpConnectionHelper.openURL(strUrl);
        try {
            if (connTimeOut > 0) {
                conn.setConnectTimeout(connTimeOut);
            }
            if (soTimeOut > 0) {
                conn.setReadTimeout(connTimeOut);
            }

            conn.addRequestProperty(HEADER_ACCEPT_ENCODING, ENCODING_VALUES);
            if (headers != null) {
                for (NameValuePair pair : headers) {
                    conn.addRequestProperty(pair.getName(), pair.getValue());
                }
            }

            int statusCode = SecurityGuardian.getResponseCode(conn);
            Log.d(TAG, "Http GET Response Code: " + statusCode + ", URL=" + strUrl);
            if (statusCode >= 200 && statusCode < 300) {
                if (len != null) {
                    len[0] = HttpConnectionHelper.getContentLength(conn);
                }

                InputStream is = SecurityGuardian.getInputStream(conn);
                if (is != null) {
                    String newHead = conn.getContentEncoding();
                    if("gzip".equalsIgnoreCase(newHead)) {
                        try {
                            is = new GZIPInputStream(is);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    result = HttpConnectionHelper.wrap(is, conn);
                }
            } else {
                throw new BadStatusCodeException(statusCode);
            }
        } finally {
            if (result == null) {
                conn.disconnect();
            }
        }

        return result;
    }

//    public static WebResourceResponse doHttpGetForWebResource(final String strUrl)
//    throws URISyntaxException, IOException {
//        if (!(URLUtil.isHttpUrl(strUrl) || URLUtil.isHttpsUrl(strUrl))) {
//            return null;
//        }
//
//        WebResourceResponse response = null;
//        InputStream result = null;
//
//        HttpURLConnection conn = HttpConnectionHelper.openURL(strUrl);
//        try {
//
//            conn.addRequestProperty(HEADER_ACCEPT_ENCODING, ENCODING_VALUES);
//
//            int statusCode = SecurityGuardian.getResponseCode(conn);
//            Log.d(TAG, "Http GET Response Code: " + statusCode + ", URL=" + strUrl);
//            if (statusCode >= 200 && statusCode < 300) {
//                InputStream is = SecurityGuardian.getInputStream(conn);
//                if (is != null) {
//                    String newHead = conn.getContentEncoding();
//                    if("gzip".equalsIgnoreCase(newHead)) {
//                        try {
//                            is = new GZIPInputStream(is);
//                        } catch (IOException e) {
//                            e.printStackTrace();
//                        }
//                    }
//                    result = HttpConnectionHelper.wrap(is, conn);
//
//                    // 获取mimeType和charset
//                    String mimeType = null;
//                    String charset = null;
//                    String contentType = conn.getContentType();
//                    if (contentType != null) {
//                        String[] contentTypeParams = contentType.trim().split(";");
//                        mimeType = contentTypeParams[0];
//                        if (contentTypeParams.length == 2) {
//                            // 解析charset
//                            String[] charsetParam = contentTypeParams[1].trim().split("=");
//                            if (charsetParam.length >= 2) {
//                                charset = charsetParam[1];
//                            }
//                        }
//                    }
//
//                    Log.d(TAG, "mimeType=" + mimeType + ", charset=" + charset);
//
//                    if (result != null) {
//                        response = new WebResourceResponse(mimeType, charset, result);
//                        if (Build.VERSION.SDK_INT >= 21) {
//                            // api 21起支持设置header
//                            try {
//                                Method methodSetHeaders = response.getClass().getMethod("setResponseHeaders", Map.class);
//                                methodSetHeaders.invoke(response, getResponseHeaders(conn));
//                            } catch (NoSuchMethodException e) {
//                                Log.e(TAG, "", e);
//                            } catch (IllegalAccessException e) {
//                                Log.e(TAG, "", e);
//                            } catch (IllegalArgumentException e) {
//                                Log.e(TAG, "", e);
//                            } catch (InvocationTargetException e) {
//                                Log.e(TAG, "", e);
//                            }
//                        }
//                    }
//                }
//            } else {
//                throw new BadStatusCodeException(statusCode);
//            }
//        } finally {
//            if (result == null) {
//                conn.disconnect();
//            }
//        }
//
//        return response;
//    }

    public static Map<String, String> getResponseHeaders(HttpURLConnection conn) {
        Map<String, String> header = new LinkedHashMap<String, String>();
        if (conn != null) {
            for (int i = 0;; i++) {
                String mine = conn.getHeaderField(i);
                if (mine == null)
                    break;
                header.put(conn.getHeaderFieldKey(i), mine);
            }
        }
        return header;
    }

    public static long getTime(InputStream in) {
        return HttpConnectionHelper.getTime(in);
    }

    /**
     * 向服务端提交HttpPost请求
     */
    public static String doHttpPost(String strUrl, List<NameValuePair> nameValuePairs) throws IOException {
        String ret = null;
        Result r = doHttpPost(strUrl, fromNameValuePairs(nameValuePairs));
        if (r != null && r.mStatus >= 200 && r.mStatus < 300) {
            try {
                ret = StreamHelper.toString(r.mContent);
            } catch (ArrayIndexOutOfBoundsException e) {
                e.printStackTrace();
            } finally {
                StreamHelper.closeSafe(r.mContent);
            }
        }
        return ret;
    }

    public static interface ConnectionWriter {
        public void write(OutputStream os) throws IOException;
    }

    public static ConnectionWriter fromString(final String str) {
        if (str == null) {
            return null;
        }

        return new ConnectionWriter() {

            @Override
            public void write(OutputStream os) throws IOException {
                try {
                    os.write(str.getBytes("UTF-8"));
                } finally {
                    StreamHelper.closeSafe(os);
                }
            }
        };
    }

    public static ConnectionWriter fromMap(final Map<String, String> data) {
        if (data == null) {
            return null;
        }
        return new ConnectionWriter() {

            @Override
            public void write(OutputStream os) throws IOException {
                OutputStreamWriter writer = new OutputStreamWriter(new BufferedOutputStream(os), "UTF-8");
                try {
                    boolean first = true;
                    for (Map.Entry<String, String> e : data.entrySet()) {
                        if (first) {
                            first = false;
                        } else {
                            writer.write("&");
                        }

                        writer.write(e.getKey());
                        writer.write("=");
                        String value = e.getValue();
                        if (value != null) {
                            writer.write(value);
                        }

                        writer.flush();
                    }
                } finally {
                    try {
                        StreamHelper.closeSafe(writer);
                    } catch (Throwable t) {
                        Log.e(TAG, "write error", t);
                    }
                    StreamHelper.closeSafe(os);
                }
            }
        };
    }

    public static ConnectionWriter fromNameValuePairs(final List<NameValuePair> data) {
        if (data == null) {
            return null;
        }
        return new ConnectionWriter() {

            @Override
            public void write(OutputStream os) throws IOException {
                OutputStreamWriter writer = new OutputStreamWriter(new BufferedOutputStream(os), "UTF-8");
                try {
                    boolean first = true;
                    for (NameValuePair pair : data) {
                        if (first) {
                            first = false;
                        } else {
                            writer.write("&");
                        }

                        writer.write(pair.getName());
                        writer.write("=");
                        String value = pair.getValue();
                        if (value != null) {
                            writer.write(value);
                        }

                        writer.flush();
                    }
                } finally {
                    try {
                        StreamHelper.closeSafe(writer);
                    } catch (Throwable t) {
                        Log.e(TAG, "write error", t);
                    }
                    StreamHelper.closeSafe(os);
                }
            }
        };
    }

    public static final class Result {
        public final Map<String, List<String>> mHeaders;
        public final InputStream mContent;
        public final int mContentLength;
        public final int mStatus;

        public Result(int status, Map<String, List<String>> headers, InputStream content, int contentLength) {
            mStatus = status;
            mContent = content;
            mHeaders = headers;
            mContentLength = contentLength;
        }
    }

    public static Result doHttpPost(String strUrl, ConnectionWriter writer) throws IOException {
        if (!(URLUtil.isHttpUrl(strUrl) || URLUtil.isHttpsUrl(strUrl))) {
            return null;
        }

        HttpURLConnection conn = HttpConnectionHelper.openURL(strUrl);
        InputStream is = null;
        try {
            if (writer != null) {
                conn.setDoOutput(true);
                OutputStream os = conn.getOutputStream();
                try {
                    writer.write(os);
                } finally {
                    StreamHelper.closeSafe(os);
                }
            }
            int statusCode = SecurityGuardian.getResponseCode(conn);
            Log.d(TAG, "Http POST Response Code: " + statusCode + ", URL=" + strUrl);
            if (statusCode >= 200 && statusCode < 300) { // Http: 2xx（成功）
                is = HttpConnectionHelper.wrap(SecurityGuardian.getInputStream(conn), conn);
                return new Result(statusCode, conn.getHeaderFields(), is, conn.getContentLength());
            }

            return new Result(statusCode, null, null, 0);
        } finally {
            if (is == null) {
                conn.disconnect();
            }
        }
    }

    /*
     * 访问服务器无需返回值
     */
    public static boolean doHttpGetRequestNothing(final String strUrl, int connTimeOut, int soTimeOut,
            List<NameValuePair> headers)
            throws URISyntaxException, IOException {
        if (!(URLUtil.isHttpUrl(strUrl) || URLUtil.isHttpsUrl(strUrl))) {
            return false;
        }

        HttpURLConnection conn = HttpConnectionHelper.openURL(strUrl);
        try {
            if (connTimeOut > 0) {
                conn.setConnectTimeout(connTimeOut);
            }
            if (soTimeOut > 0) {
                conn.setReadTimeout(connTimeOut);
            }

            if (headers != null) {
                for (NameValuePair pair : headers) {
                    conn.addRequestProperty(pair.getName(), pair.getValue());
                }
            }

            int statusCode = SecurityGuardian.getResponseCode(conn);
            Log.d(TAG, "Http GET Response Code: " + statusCode + ", URL=" + strUrl);
            return statusCode >= 200 && statusCode < 300;
        } finally {
            conn.disconnect();
        }
    }

    public static HttpURLConnection openConnection(URL url) throws MalformedURLException, IOException {
        return HttpConnectionHelper.openURL(url);
    }

    public static void setNetworkAllow(boolean allow) {
        synchronized (sNetWorkPermissionLock) {
            if (sHandler != null) {
                sHandler.setNetworkAllow(allow);
            }
        }
    }

    public static boolean isNetworkAllow() {
        synchronized (sNetWorkPermissionLock) {
            if (sHandler != null) {
                return sHandler.isNetworkAllow();
            }
        }
        return true;
    }

    public static interface NetworkPermissionHandler {
        public void setNetworkAllow(boolean allow);
        public boolean isNetworkAllow();
    }

    private static NetworkPermissionHandler sHandler;
    public static void setNetworkPermissionHandler(NetworkPermissionHandler handler) {
        synchronized (sNetWorkPermissionLock) {
            sHandler = handler;
        }
    }

    /**
     * 向服务端提交HttpGet请求
     */
    public static long doGetHostTime(final String strUrl) {
        long hostTime = 0;
        if (!(URLUtil.isHttpUrl(strUrl) || URLUtil.isHttpsUrl(strUrl))) {
            return hostTime;
        }

        HttpURLConnection conn = null;
        try {
            conn = HttpConnectionHelper.openURL(strUrl);
            int statusCode = SecurityGuardian.getResponseCode(conn);
            Log.d(TAG, "Http GET Response Code: " + statusCode + ", URL=" + strUrl);
            if (statusCode >= 200 && statusCode < 300) {
                hostTime = conn.getDate();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return hostTime;
    }

//    public static byte[] toPostBody(List<NameValuePair> params) {
//        ConnectionWriter writer = fromNameValuePairs(params);
//        DirectlyOutputStream dos = new DirectlyOutputStream();
//        try {
//            writer.write(dos);
//            return dos.toByteArray();
//        } catch (IOException e) {
//        }
//
//        return null;
//    }

    public static String getUserAgent() {
        return HttpConnectionHelper.USER_AGENT;
    }
}
